<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      class Super {
        constructor(concurrencyCount = 2) {
          // 允许同时执行的数量
          this.concurrency = concurrency;
          // 任务列表
          this.taskList = [];
          // 正在执行的任务数量
          this.currentCount = 0;
        }

        // 添加任务 要返回当前添加任务的promise
        add(task) {
          return new Promise((resolve, reject) => {
            this.taskList.push({ task, resolve, reject });
            this.run();
          });
        }

        run() {
          // 当前正在执行的数量小于限制并发数，并且任务列表里面没有任务就可以执行
          while (this.currentCount < this.concurrency && this.taskList.length > 0) {
            this.currentCount++;
            const { task, resolve, reject } = this.taskList.shift();
            //   需要在run函数中控制add函数什么时候返回这个promise，所以需要把resolve, reject都传过去
            task().then(resolve, reject).finally();
          }
        }
      }

      //  延迟函数delay 模拟异步请求
      function timeOut(time) {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve();
          }, time);
        });
      }

      const s = new Super();
      function addTask(time, task) {
        // 通过add传异步任务到实例对象的任务队列里面
        s.add(() => {
          return timeOut(time);
        }).then(() => {
          console.log(`任务${task}完成`);
        });
      }

      addTask(1000, "1");
      addTask(200, "2");
      addTask(400, "3");
      addTask(600, "4");
      addTask(300, "5");


      
      class Scheduler {
        constructor(max) {
          this.max = max;
          this.count = 0; //用来记录当前正在执行的异步函数
          this.queue = new Array(); //表示等待队列
        }
        async add(promiseCreator) {
          /*
        此时count已经满了，不能执行本次add需要阻塞在这里，将resolve放入队列中等待唤醒,
        等到count<max时，从队列中取出执行resolve,执行，await执行完毕，本次add继续
        */
          if (this.count >= this.max) await new Promise((resolve, reject) => this.queue.push(resolve));

          this.count++;
          let res = await promiseCreator();
          this.count--;
          if (this.queue.length) {
            //依次唤醒add
            // 若队列中有值，将其resolve弹出，并执行
            // 以便阻塞的任务，可以正常执行
            this.queue.shift()();
          }
          return res;
        }
      }

      const timeout = (time) =>
        new Promise((resolve) => {
          setTimeout(resolve, time);
        });

      const scheduler = new Scheduler(2);

      const addTask = (time, order) => {
        //add返回一个promise，参数也是一个promise
        scheduler.add(() => timeout(time)).then(() => console.log(order));
      };

      addTask(1000, "1");
      addTask(500, "2");
      addTask(300, "3");
      addTask(400, "4");
    </script>
  </body>
</html>
